package io.kwdb.examples;

import io.kwdb.sdk.*;
import java.util.*;
import java.util.logging.*;

/**
 * Example 05: Query with Typed Access
 *
 * <p>Demonstrates: - Writing time-series data with various types - Executing queries with SQL -
 * Accessing result data with proper type conversion - Working with different data types (long,
 * double, string, boolean) - Handling query errors and empty results
 */
public class Example05_QueryTypedDemo {

  private static final Logger logger = Logger.getLogger(Example05_QueryTypedDemo.class.getName());

  public static void main(String[] args) throws Exception {
    System.out.println("=======================================");
    System.out.println(" KWDB Java SDK M3 - Query Typed Demo");
    System.out.println("=======================================\n");

    // Configure logging
    Logger.getLogger("io.kwdb").setLevel(Level.INFO);

    // Test with gRPC client (supports both write and query)
    testWithGrpcClient();

    System.out.println("\n=======================================");
    System.out.println(" Query Typed Demo Completed");
    System.out.println("=======================================");
  }

  /** Test query functionality with gRPC client. */
  private static void testWithGrpcClient() throws Exception {
    System.out.println("--- Testing with gRPC Client ---\n");

    Config config =
        Config.builder()
            .host("127.0.0.1")
            .port(9876) // gRPC port
            .transport(Config.Transport.GRPC)
            .timeoutMillis(5000)
            .build();

    try (KwdbClient client = KwdbClient.create(config)) {
      // Step 1: Write sample data
      writeSampleData(client);

      // Step 2: Query and demonstrate typed access
      demonstrateQueries(client);

      // Step 3: Demonstrate aggregation queries
      demonstrateAggregations(client);

      // Step 4: Demonstrate error handling
      demonstrateErrorHandling(client);
    }
  }

  /** Write sample time-series data. */
  private static void writeSampleData(KwdbClient client) {
    System.out.println("1. Writing sample time-series data...");

    long baseTime = System.currentTimeMillis() * 1_000_000; // Convert to nanos

    // Server metrics data
    for (int i = 0; i < 20; i++) {
      Point point =
          Point.builder("server_metrics")
              .tag("host", "server-" + (i % 3))
              .tag("region", i < 10 ? "us-west" : "us-east")
              .tag("environment", i % 2 == 0 ? "production" : "staging")
              .field("cpu_usage", 20.0 + Math.random() * 60) // 20-80%
              .field("memory_gb", 4.0 + Math.random() * 12) // 4-16 GB
              .field("disk_io_mbps", Math.random() * 500) // 0-500 Mbps
              .field("network_packets", (long) (Math.random() * 10000))
              .field("error_count", (long) (Math.random() * 10))
              .field("is_healthy", Math.random() > 0.1) // 90% healthy
              .timestamp(baseTime + i * 60_000_000_000L) // 1 minute apart
              .build();

      Status status = client.write(point);
      if (!status.isOk()) {
        System.err.println("Failed to write point: " + status.getMessage());
      }
    }

    // Application metrics
    for (int i = 0; i < 10; i++) {
      Point point =
          Point.builder("app_metrics")
              .tag("service", i % 2 == 0 ? "api-gateway" : "data-processor")
              .tag("version", "v1.2." + (i % 3))
              .field("request_count", (long) (100 + Math.random() * 900))
              .field("avg_latency_ms", 10.0 + Math.random() * 90)
              .field("success_rate", 0.95 + Math.random() * 0.05)
              .field("active_connections", (long) (Math.random() * 100))
              .timestamp(baseTime + i * 30_000_000_000L) // 30 seconds apart
              .build();

      Status status = client.write(point);
      if (!status.isOk()) {
        System.err.println("Failed to write point: " + status.getMessage());
      }
    }

    System.out.println("✓ Wrote 30 data points across 2 measurements\n");
  }

  /** Demonstrate various queries with typed access. */
  private static void demonstrateQueries(KwdbClient client) {
    System.out.println("2. Demonstrating queries with typed access...\n");

    // Query 1: Select all server metrics
    System.out.println("Query 1: Recent server metrics");
    String sql1 = "SELECT * FROM server_metrics ORDER BY time DESC LIMIT 5";
    QueryResult result1 = client.query(sql1);

    if (result1.isSuccess()) {
      System.out.println("Columns: " + result1.getColumns());
      System.out.println("Row count: " + result1.getRowCount());

      // Access data with proper types
      for (int i = 0; i < result1.getRowCount(); i++) {
        Map<String, Object> row = result1.getRow(i);
        String host = (String) row.get("host");
        Double cpu = result1.getDouble(i, "cpu_usage");
        Long packets = result1.getLong(i, "network_packets");
        Boolean healthy = result1.getBoolean(i, "is_healthy");

        System.out.printf(
            "  Row %d: host=%s, cpu=%.2f%%, packets=%d, healthy=%s%n",
            i, host, cpu, packets, healthy);
      }
    } else {
      System.err.println("Query failed: " + result1.getError().getMessage());
    }
    System.out.println();

    // Query 2: Filter by tags
    System.out.println("Query 2: Production servers in us-west");
    String sql2 =
        "SELECT host, cpu_usage, memory_gb FROM server_metrics "
            + "WHERE region = 'us-west' AND environment = 'production' "
            + "ORDER BY time DESC LIMIT 5";
    QueryResult result2 = client.query(sql2);

    if (result2.isSuccess()) {
      System.out.println("Found " + result2.getRowCount() + " rows:");
      for (QueryResult.Row row : result2.getRows()) {
        System.out.printf(
            "  %s: CPU=%.2f%%, Memory=%.2fGB%n",
            row.getString("host"), row.getDouble("cpu_usage"), row.getDouble("memory_gb"));
      }
    }
    System.out.println();

    // Query 3: Time range query
    System.out.println("Query 3: App metrics with high latency");
    String sql3 =
        "SELECT service, version, avg_latency_ms, request_count "
            + "FROM app_metrics "
            + "WHERE avg_latency_ms > 50 "
            + "ORDER BY avg_latency_ms DESC";
    QueryResult result3 = client.query(sql3);

    if (result3.isSuccess()) {
      System.out.println("High latency services:");
      for (int i = 0; i < result3.getRowCount(); i++) {
        System.out.printf(
            "  %s (%s): %.2fms latency, %d requests%n",
            result3.getString(i, "service"),
            result3.getString(i, "version"),
            result3.getDouble(i, "avg_latency_ms"),
            result3.getLong(i, "request_count"));
      }
    }
    System.out.println();
  }

  /** Demonstrate aggregation queries. */
  private static void demonstrateAggregations(KwdbClient client) {
    System.out.println("3. Demonstrating aggregation queries...\n");

    // Aggregation 1: Average metrics by host
    System.out.println("Aggregation 1: Average CPU by host");
    String sql1 =
        "SELECT host, "
            + "AVG(cpu_usage) as avg_cpu, "
            + "MAX(cpu_usage) as max_cpu, "
            + "MIN(cpu_usage) as min_cpu "
            + "FROM server_metrics "
            + "GROUP BY host";
    QueryResult result1 = client.query(sql1);

    if (result1.isSuccess()) {
      for (QueryResult.Row row : result1.getRows()) {
        System.out.printf(
            "  %s: avg=%.2f%%, max=%.2f%%, min=%.2f%%%n",
            row.getString("host"),
            row.getDouble("avg_cpu"),
            row.getDouble("max_cpu"),
            row.getDouble("min_cpu"));
      }
    }
    System.out.println();

    // Aggregation 2: Count by region
    System.out.println("Aggregation 2: Data points by region");
    String sql2 = "SELECT region, COUNT(*) as count " + "FROM server_metrics " + "GROUP BY region";
    QueryResult result2 = client.query(sql2);

    if (result2.isSuccess()) {
      for (QueryResult.Row row : result2.getRows()) {
        System.out.printf("  %s: %d points%n", row.getString("region"), row.getLong("count"));
      }
    }
    System.out.println();

    // Aggregation 3: Service statistics
    System.out.println("Aggregation 3: Service performance stats");
    String sql3 =
        "SELECT service, "
            + "COUNT(*) as samples, "
            + "AVG(success_rate) * 100 as avg_success_pct, "
            + "SUM(request_count) as total_requests "
            + "FROM app_metrics "
            + "GROUP BY service";
    QueryResult result3 = client.query(sql3);

    if (result3.isSuccess()) {
      for (QueryResult.Row row : result3.getRows()) {
        System.out.printf(
            "  %s: %d samples, %.2f%% success, %d total requests%n",
            row.getString("service"),
            row.getLong("samples"),
            row.getDouble("avg_success_pct"),
            row.getLong("total_requests"));
      }
    }
    System.out.println();
  }

  /** Demonstrate error handling in queries. */
  private static void demonstrateErrorHandling(KwdbClient client) {
    System.out.println("4. Demonstrating error handling...\n");

    // Test 1: Invalid SQL syntax
    System.out.println("Test 1: Invalid SQL syntax");
    String badSql = "SELECT * FORM invalid_table"; // Typo: FORM instead of FROM
    QueryResult result = client.query(badSql);
    if (!result.isSuccess()) {
      System.out.println("✓ Caught syntax error: " + result.getError().getMessage());
    }
    System.out.println();

    // Test 2: Non-existent table
    System.out.println("Test 2: Query non-existent table");
    String noTable = "SELECT * FROM non_existent_table";
    result = client.query(noTable);
    if (!result.isSuccess()) {
      System.out.println("✓ Caught table not found: " + result.getError().getMessage());
    }
    System.out.println();

    // Test 3: Empty result set
    System.out.println("Test 3: Empty result set");
    String emptyQuery = "SELECT * FROM server_metrics WHERE cpu_usage > 999";
    result = client.query(emptyQuery);
    if (result.isSuccess()) {
      System.out.println("✓ Query succeeded with " + result.getRowCount() + " rows (empty)");
    }
    System.out.println();

    // Test 4: Type conversion error handling
    System.out.println("Test 4: Safe type conversion");
    String mixedQuery = "SELECT host, cpu_usage FROM server_metrics LIMIT 1";
    result = client.query(mixedQuery);
    if (result.isSuccess() && result.getRowCount() > 0) {
      // Safe conversion with default values
      String host = result.getString(0, "host");
      Double cpu = result.getDouble(0, "cpu_usage");
      Long wrongType = result.getLong(0, "cpu_usage"); // cpu_usage is double, not long

      System.out.println("  host (string): " + host);
      System.out.println("  cpu_usage (double): " + cpu);
      System.out.println("  cpu_usage as long (conversion): " + wrongType);
      System.out.println("✓ Type conversions handled safely");
    }
  }

  /** Helper method to print query results in table format. */
  private static void printResultTable(QueryResult result) {
    if (!result.isSuccess()) {
      System.err.println("Query failed: " + result.getError().getMessage());
      return;
    }

    List<QueryResult.Column> columns = result.getColumns();
    List<String> columnNames = new ArrayList<>();
    for (QueryResult.Column col : columns) {
      columnNames.add(col.getName());
    }

    // Print header
    System.out.println("+" + "-".repeat(80) + "+");
    System.out.print("|");
    for (QueryResult.Column col : columns) {
      System.out.printf(" %-15s |", col.getName());
    }
    System.out.println();
    System.out.println("+" + "-".repeat(80) + "+");

    // Print rows
    for (QueryResult.Row row : result.getRows()) {
      System.out.print("|");
      for (QueryResult.Column col : columns) {
        Object value = row.getValue(col.getName());
        if (value instanceof Double) {
          System.out.printf(" %15.2f |", value);
        } else if (value instanceof Long) {
          System.out.printf(" %15d |", value);
        } else if (value instanceof Boolean) {
          System.out.printf(" %15s |", value);
        } else {
          System.out.printf(" %-15s |", value != null ? value.toString() : "null");
        }
      }
      System.out.println();
    }
    System.out.println("+" + "-".repeat(80) + "+");
  }
}
